#pragma once

#include "footage.h"
#include "still_image_id.h"
#include "file_sequence.h"
#include "mswin/work_thread.h"
#include "mswin/order_queue.h"

namespace icache {

class coordinator;

struct img_read_order {
	img_read_order() { }
	img_read_order(int n, int p) : frame_num(n), priority(p) { }
	int frame_num;
	int priority;  ///< same units of measurement as in Win's SetThreadPriorit(...) method
};

class img_read_order_queue : public mswin::OrderQueue<img_read_order>
{
public:
	bool Push(img_read_order const & o);
};

class subscriptions;
class seq_reader;

class seq_frame_request : public still_image_id
{
public: //item_id interface
	virtual bool loadable() const { return false; }
	virtual void load(byte_image & im) const;
	virtual bool requestable() const { return true; }
	virtual void request() const;
public:
	seq_reader & reader;
	int frame_num;
	nearby desired;
	int priority;
public:
	seq_frame_request(seq_reader & r, int n, nearby may_be_requested_later, int p);
	static item_id_sptr create(seq_reader & r, int n, nearby may_be_requested_later, int p)
		{ return item_id_sptr(new seq_frame_request(r, n, may_be_requested_later, p)); }
};

class seq_reader : public footage_simple_impl, public mswin::WorkThread<img_read_order_queue>
{
public:
	typedef mswin::WorkThread<img_read_order_queue> superclass;
public: //footage inerface
	virtual string filepath() const;
	virtual item_id_sptr frame_id(int frame);
	virtual cached_image_sptr get(int frame, nearby may_be_requested_later = no_nearby, bool wait = true);
	virtual cached_image_sptr try_get(int frame);
	virtual icache::cache * cache() const;
	virtual xml::stream & serialize(xml::stream & s, string const & reference_path);
	virtual string description() const;
	virtual int size() const;
	virtual int first_frame_num() const;
	virtual size_extent dim() const;
	virtual bool alpha_channel() const;
	virtual fraction fps() const;
public:
	seq_reader(const string & filename = string(), icache::coordinator * c = 0);
	seq_reader(const file_sequence & seq, icache::coordinator * c = 0);
	virtual ~seq_reader();
	void open(const file_sequence & seq);
	void open_by_example(const string & filename);
	void open_by_filter(const string & filter);
	void close();
	bool is_open() const;
	void coordinator(icache::coordinator * c);
	icache::coordinator * coordinator() const;
	int duration() const;
	string const & basepath() const; ///< slash ended
	string const & relative_framepath(int n) const;
	string full_framepath(int n) const;
public:
	//if priority of order is larger than the current thread's priority, then boosts thread's priority
	void request(const OrderType & o);
	void request(int frame, nearby may_be_requested_later, int priority);
	///call this function after an order have placed, the function returns when the requested frame will be in cache
	void wait_for_frame(int frame);
	void request_and_wait(int frame, nearby may_be_requested_later, int priority);
private:
	using superclass::Place;
	virtual void Execute(const OrderType & o);
private:
	icache::coordinator * _coordinator;
	std::auto_ptr<subscriptions> _subscriptions;
	file_sequence _seq;
	size_extent _dim;
	bool _alpha;
};

//inlines

inline int seq_reader::duration() const
	{ return _seq.duration(); }

inline bool seq_reader::is_open() const
	{ return !_seq.empty(); }

inline string const & seq_reader::basepath() const
	{ return _seq.basepath(); }

inline string const & seq_reader::relative_framepath(int n) const
	{ return _seq.relative_framepath(n); }

inline string seq_reader::full_framepath(int n) const
	{ return _seq.full_framepath(n); }

inline icache::coordinator * seq_reader::coordinator() const
	{ return _coordinator; }

} //namespace icache
